package com.j.lemon.learn.utils;

import com.j.lemon.learn.SnowflakeIdGenerator;

public class SequenceUtil {
    private SequenceUtil() {
    }

    public static String genSeqId() {

        SnowflakeIdGenerator instance = SnowflakeIdGenerator.getInstance();
        long longId = instance.nextId();
        return String.valueOf(longId);
    }

    public static Long genLongSeqId() {
        return SnowflakeIdGenerator.getInstance().nextId();
    }

    static class SnowflakeIdGenerator {
        //================================================Algorithm's Parameter=============================================
        /**
         * 机器id所占的位数
         */
        private final long workerIdBits = 5L;
        /**
         * 数据标识id所占的位数
         */
        private final long dataCenterIdBits = 5L;
        //=================================================Works's Parameter================================================
        /**
         * 工作机器ID(0~31)
         */
        private final long workerId;
        /**
         * 数据中心ID(0~31)
         */
        private final long dataCenterId;
        /**
         * 毫秒内序列(0~4095)
         */
        private long sequence = 0L;
        /**
         * 上次生成ID的时间截
         */
        private long lastTimestamp = -1L;


        private static volatile SnowflakeIdGenerator snowflakeIdGenerator;

        public static SnowflakeIdGenerator getInstance() {
            if (snowflakeIdGenerator == null) {
                synchronized (SequenceUtil.class) {
                    if (snowflakeIdGenerator == null) {
                        snowflakeIdGenerator = new SnowflakeIdGenerator(0, 0);
                    }
                }
            }
            return snowflakeIdGenerator;
        }

        //===============================================Constructors=======================================================

        /**
         * 构造函数
         *
         * @param workerId     工作ID (0~31)
         * @param dataCenterId 数据中心ID (0~31)
         */
        public SnowflakeIdGenerator(long workerId, long dataCenterId) {
            // 支持的最大机器id(十进制)，结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
            // -1L 左移 5位 (worker id 所占位数) 即 5位二进制所能获得的最大十进制数 - 31
            long maxWorkerId = ~(-1L << workerIdBits);
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("Worker Id can't be greater than %d or less than 0", maxWorkerId));
            }
            long maxDataCenterId = ~(-1L << dataCenterIdBits);
            if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
                throw new IllegalArgumentException(String.format("DataCenter Id can't be greater than %d or less than 0", maxDataCenterId));
            }
            this.workerId = workerId;
            this.dataCenterId = dataCenterId;
        }
        // ==================================================Methods========================================================

        /**
         * 线程安全的获得下一个 ID 的方法
         */
        public synchronized long nextId() {
            long timestamp = currentTime();
            //如果当前时间小于上一次ID生成的时间戳: 说明系统时钟回退过 - 这个时候应当抛出异常
            if (timestamp < lastTimestamp) {
                throw new RuntimeException(
                        String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            }
            //如果是同一时间生成的，则进行毫秒内序列
            long sequenceBits = 12L;
            if (lastTimestamp == timestamp) {
                // 生成序列的掩码(12位所对应的最大整数值)，这里为4095 (0b111111111111=0xfff=4095)
                long sequenceMask = ~(-1L << sequenceBits);
                sequence = (sequence + 1) & sequenceMask;
                //毫秒内序列溢出 即 序列 > 4095
                if (sequence == 0) {
                    //阻塞到下一个毫秒,获得新的时间戳
                    timestamp = blockTillNextMillis(lastTimestamp);
                }
            }
            //时间戳改变，毫秒内序列重置
            else {
                sequence = 0L;
            }
            //上次生成ID的时间截
            lastTimestamp = timestamp;
            //移位并通过或运算拼到一起组成64位的ID
            //时间截向 左移位数 - 22(5+5+12)
            long timestampMoveBits = sequenceBits + workerIdBits + dataCenterIdBits;
            //数据标识id 左移位数 - 17(12+5)
            long dataCenterIdMoveBits = sequenceBits + workerIdBits;
            long startTime = 1498608000000L;
            return ((timestamp - startTime) << timestampMoveBits)
                    | (dataCenterId << dataCenterIdMoveBits)
                    | (workerId << sequenceBits)
                    | sequence;
        }

        /**
         * 阻塞到下一个毫秒 即 直到获得新的时间戳
         */
        protected long blockTillNextMillis(long lastTimestamp) {
            long timestamp = currentTime();
            while (timestamp <= lastTimestamp) {
                timestamp = currentTime();
            }
            return timestamp;
        }

        /**
         * 获得以毫秒为单位的当前时间
         */
        protected long currentTime() {
            return System.currentTimeMillis();
        }

    }
    
}
